//
//  PSTestFilterConsecutiveCards.m
//  PrefCore
//
//  Created by Sergey on 10/3/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "PSFilter.h"
#import "PSCard.h"

#define VALID_PAIR_COUNT 6561


static struct {
    PSCardRankSet hand;
    PSCardRankSet trash;
} valid_pair[6561], *valid_pair_end;

@interface PSFilterConsecutiveCardsTests : XCTestCase

@end

@implementation PSFilterConsecutiveCardsTests

-(void)setUp {
    valid_pair_end = valid_pair;
    for (uint16_t maskHand = 0 ; maskHand <= 0xFF ; maskHand++) {
        for (uint16_t maskTrash = 0 ; maskTrash <= 0xFF ; maskTrash++) {
            if ((maskHand & maskTrash) != 0) {
                continue;
            }
            valid_pair_end->hand = (PSCardRankSet)maskHand;
            valid_pair_end->trash = (PSCardRankSet)maskTrash;
            valid_pair_end++;
        }
    }
    // Check that we have filled all possible pairs
    XCTAssertEqual(VALID_PAIR_COUNT, valid_pair_end-valid_pair);
}

-(void)testSingleSuit {
    for (int i = 0 ; i != PS_CARD_SUIT_RISING_LEN ; i++) {
        PSCardSuit suit = PSCardSuitRising[i];
        for (int j = 0 ; j != VALID_PAIR_COUNT ; j++) {
            PSCardRankSet handOnly = valid_pair[j].hand;
            PSHandHash hand = PSHandAddSuitFromSet(0, handOnly, suit);
            PSCardRankSet trashOnly = valid_pair[j].trash;
            PSHandHash trash = PSHandAddSuitFromSet(0, trashOnly, suit);
            PSHandHash filteredMin = PSFilterSameSuitConsecutiveCardsMin(hand, trash);
            PSCardRankSet minCards = PSHandGetSuit(filteredMin, suit);
            [self checkIsMin:minCards hand:handOnly trash:trashOnly];
            PSHandHash filteredMax = PSFilterSameSuitConsecutiveCardsMax(hand, trash);
            PSCardRankSet maxCards = PSHandGetSuit(filteredMax, suit);
            [self checkIsMax:maxCards hand:handOnly trash:trashOnly];
        }
    }
}

-(void)testTimingMin {
    [self measureBlock:^{
        for (int r = 0 ; r != 10 ; r++) {
            for (int i = 0 ; i != PS_CARD_SUIT_RISING_LEN ; i++) {
                PSCardSuit suit = PSCardSuitRising[i];
                for (int j = 0 ; j != VALID_PAIR_COUNT ; j++) {
                    PSHandHash hand = PSHandAddSuitFromSet(0, valid_pair[j].hand, suit);
                    PSHandHash trash = PSHandAddSuitFromSet(0, valid_pair[j].trash, suit);
                    PSFilterSameSuitConsecutiveCardsMin(hand, trash);
                }
            }
        }
    }];
}

-(void)testTimingMax {
    [self measureBlock:^{
        for (int r = 0 ; r != 10 ; r++) {
            for (int i = 0 ; i != PS_CARD_SUIT_RISING_LEN ; i++) {
                PSCardSuit suit = PSCardSuitRising[i];
                for (int j = 0 ; j != VALID_PAIR_COUNT ; j++) {
                    PSHandHash hand = PSHandAddSuitFromSet(0, valid_pair[j].hand, suit);
                    PSHandHash trash = PSHandAddSuitFromSet(0, valid_pair[j].trash, suit);
                    PSFilterSameSuitConsecutiveCardsMax(hand, trash);
                }
            }
        }
    }];
}

-(void)checkIsMin:(PSCardRankSet)minCards hand:(PSCardRankSet)hand trash:(PSCardRankSet)trash {
    XCTAssertTrue((minCards & trash) == 0, @"Cards must not be coming from the trash.");
    XCTAssertTrue(minCards != 0 || hand == 0, @"Filtered result is empty for a non-empty hand.");
    XCTAssertTrue((minCards & hand) == minCards, @"Some of the filtered cards do not belong to the hand.");
    for (uint8_t m = 0x80 ; m != 0 ; m >>= 1) {
        if ((m & minCards) != 0) {
            XCTAssertTrue(m == 1 || ((m >> 1) & hand) == 0, @"Another card in hand is smaller.");
        } else {
            if ((m & hand) != 0) {
                XCTAssertTrue(m != 1, @"Smallest card in the hand must be included.");
                XCTAssertTrue(((m >> 1) & (hand | trash)) != 0, @"This card must be included.");
                for (uint8_t mm = m>>1 ; mm != 0 ; mm >>= 1) {
                    if ((minCards & mm) != 0) {
                        // We found a smaller card in the filtered set
                        break;
                    }
                    XCTAssertTrue(((hand & mm) != 0) || ((trash & mm) != 0), @"Chain ends without a min card.");
                }
            }
        }
    }
}

-(void)checkIsMax:(PSCardRankSet)maxCards hand:(PSCardRankSet)hand trash:(PSCardRankSet)trash {
    XCTAssertTrue((maxCards & trash) == 0, @"Cards must not be coming from the trash.");
    XCTAssertTrue(maxCards != 0 || hand == 0, @"Filtered result is empty for a non-empty hand.");
    XCTAssertTrue((maxCards & hand) == maxCards, @"Some of the filtered cards do not belong to the hand.");
    for (uint8_t m = 0x01 ; m != 0 ; m <<= 1) {
        if ((m & maxCards) != 0) {
            XCTAssertTrue(m == 0x80 || ((m << 1) & hand) == 0, @"Another card in hand is greater.");
        } else {
            if ((m & hand) != 0) {
                XCTAssertTrue(m != 0x80, @"Highest card in the hand must be included.");
                XCTAssertTrue(((m << 1) & (hand | trash)) != 0, @"This card must be included.");
                for (uint8_t mm = m<<1 ; mm != 0 ; mm <<= 1) {
                    if ((maxCards & mm) != 0) {
                        // We found a higher card in the filtered set
                        break;
                    }
                    XCTAssertTrue(((hand & mm) != 0) || ((trash & mm) != 0), @"Chain ends without a min card.");
                }
            }
        }
    }
}

@end


